//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <iostream>
#include <fstream>

#include <curl/curl.h>
#include <zlib.h>
#include <mingpp.h>

#include "libraries.h"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define LENGTHINLINE    10000
#define DELAYTIMESECOND 120
#define DELAYPOSITION   10000
#define URLTOPMATCH     "http://topmatch.services.came.sbg.ac.at/TopMatchFlex.swf?query=%s&target=%s\0"
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
static char   errorBuffer[CURL_ERROR_SIZE];
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
static int writer(char *data, size_t size, size_t nmemb, string *buffer) {
  int result = 0;
  if (buffer != NULL) {
    buffer->append(data, size * nmemb);
    result = size * nmemb;
  }
  return result;
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
string EraseHTMLTAG(string text) {

  size_t  foundB, foundE;

  do {
    foundB = text.find("<");             if (foundB == string::npos) break;
    foundE = text.find(">", foundB + 1); if (foundE == string::npos) break;
    text.erase(int(foundB), int(foundE));
  } while (true);

  return text;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* SplitColumn(char* buffer, int position, const char* characters) {

  if (!buffer || !characters || (position <= 0)) return NULL;

  char *part = strtok(buffer, characters);
  if (position == 1) return part;

  int tmpPosition = 1;
  do {
    part = strtok(NULL, characters);
    tmpPosition++;
  } while (part && (tmpPosition != position));

  return part;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void ShowTime(time_t initialTime) {
    
    time_t endTime;    
    time(&endTime);
     
    int timeSeconds = (int)difftime(endTime, initialTime);
    int timeMinutes = timeSeconds/60;    
    int timeHours   = timeMinutes/60;    
    int timeDays    = timeHours/24;    
    
    timeSeconds    %= 60;    
    timeMinutes    %= 60;    
    timeHours      %= 24;   
    
    printf("Elapsed Time: %02d:%02d:%02d:%02d (DD:HH:MM:SS) |", timeDays, timeHours, timeMinutes, timeSeconds);    
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
char* ShowTime(time_t initialTime, char* buffer) {
    
    time_t endTime;    
    time(&endTime);
     
    int timeSeconds = (int)difftime(endTime, initialTime);
    int timeMinutes = timeSeconds/60;    
    int timeHours   = timeMinutes/60;    
    int timeDays    = timeHours/24;    
    
    timeSeconds    %= 60;    
    timeMinutes    %= 60;    
    timeHours      %= 24;   
    
    sprintf(buffer, "Elapsed Time: %02d:%02d:%02d:%02d (DD:HH:MM:SS) |\0", timeDays, timeHours, timeMinutes, timeSeconds);    
    
    return buffer;
    
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void Wait(int seconds) {
  clock_t endwait;
  endwait = clock () + seconds * CLOCKS_PER_SEC ;
  while (clock() < endwait) {}
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
int main (int argc, char** argv) {

  char*  namefileIn  = NULL;
  char*  namefileOut = NULL;
  
  time_t initialTime;
  
  initialTime = time(NULL);

  if (argc < 3) {
    cout << "Error in the input parameters! Must by like: " << endl;
    cout << "./TopMatchCompute <Input File with one column with the following format \"pdbid,chain\"> <Output Similarity Matrix File>" << endl;
    return 0;
  }
  else {
    cout << "---------------------------------------------------------------------------------" << endl;
    cout << "  Input File : " << argv[1] << endl;
    cout << " Output File : " << argv[2] << endl;
    
    namefileIn  = new char[strlen(argv[1]) + 10];
    namefileOut = new char[strlen(argv[2]) + 10];
    strcpy(namefileIn,  argv[1]);
    strcpy(namefileOut, argv[2]);
  }

  fstream fileIn, fileOutS, fileOuts, fileOutCq, fileOutCt, fileOutRMS, fileOutIdentity, fileLog;
  char    buffer[LENGTHINLINE + 1];

  fileIn.open(namefileIn, fstream::in);

  if (!fileIn.is_open()) {
    if (namefileIn)  delete[] namefileIn;
    if (namefileOut) delete[] namefileOut;
    return 0;
  }
  
  TListE <String> *listFiles = new TListE <String> ();
  
  while (!fileIn.eof()) {
    fileIn.getline(buffer, LENGTHINLINE);
    String* nameFile = new String(buffer);
    nameFile->Trim();
    if (nameFile->Length()) listFiles->Add(nameFile);
  }
  fileIn.close();
  
  char* completeName = new char[strlen(namefileOut) + 30];
  
  sprintf(completeName, "%s_S.dat\0", namefileOut);
  fileOutS.open(completeName, fstream::out);
  cout << "Output Number of residue pairs File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  sprintf(completeName, "%s_s.dat\0", namefileOut);
  fileOuts.open(completeName, fstream::out);
  cout << "Output Relative similarity File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  sprintf(completeName, "%s_Cq.dat\0", namefileOut);
  fileOutCq.open(completeName, fstream::out);
  cout << "Output Relative cover of the query with respect to the target File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  sprintf(completeName, "%s_Ct.dat\0", namefileOut);
  fileOutCt.open(completeName, fstream::out);
  cout << "Output Relative cover of the target with respect to the query File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  sprintf(completeName, "%s_RMS.dat\0", namefileOut);
  fileOutRMS.open(completeName, fstream::out);
  cout << "Output Root-mean-square error of superposition in Ångströms File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;

  sprintf(completeName, "%s_Identity.dat\0", namefileOut);
  fileOutIdentity.open(completeName, fstream::out);
  cout << "Output Sequence identity of query and target in the equivalent regions File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;
  
  sprintf(completeName, "%s.log\0", namefileOut);
  fileLog.open(completeName, fstream::out);
  cout << "Log File :\n " << completeName << endl;
  cout << "---------------------------------------------------------------------------------" << endl;
  
  if (completeName) delete[] completeName;

  if (!fileOutS.is_open()  || !fileOuts.is_open()   || !fileOutCq.is_open() || 
      !fileOutCt.is_open() || !fileOutRMS.is_open() || !fileOutIdentity.is_open()) {
    if (namefileIn)  delete[] namefileIn;
    if (namefileOut) delete[] namefileOut;
    if (listFiles)   delete   listFiles;
    return 0;
  }

  cout << "S" << endl;
  cout << "  Number of residue pairs that are structurally equivalent" << endl;
  cout << "  (= length of the alignment, similarity)" << endl;
  cout << "s" << endl;
  cout << "  Relative similarity, expressed in percent" << endl;
  cout << "  (= 100 x (2 x S/( Lq+Lt)), where Lq and Lt are the lengths of the query and target sequences, respectively)" << endl;
  cout << "cq" << endl;
  cout << "  Relative cover of the query with respect to the target, expressed in percent" << endl;
  cout << "  (= 100 x S/Lq)" << endl;
  cout << "ct" << endl;
  cout << "  Relative cover of the target with respect to the query, expressed in percent" << endl;
  cout << "  (= 100 x S/Lt)" << endl;
  cout << "RMS" << endl;
  cout << "  Root-mean-square error of superposition in Ångströms, calculated using all structurally equivalent C-alpha atoms." << endl;
  cout << "Identity" << endl;
  cout << "  Sequence identity of query and target in the equivalent regions, expressed in percent." << endl;
  cout << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  ShowTime(initialTime);
  cout.width(14); cout.right; cout << "        Target |";
  cout.width(14); cout.right; cout << "         Query |";
  cout.width(8);  cout.right; cout << "    Rank |";
  cout.width(8);  cout.right; cout << "       S |";
  cout.width(8);  cout.right; cout << "       s |";
  cout.width(8);  cout.right; cout << "      Cq |";
  cout.width(8);  cout.right; cout << "      Ct |";
  cout.width(8);  cout.right; cout << "     RMS |";
  cout.width(12); cout.right; cout << "   Similarity|";
  cout << endl;
  cout << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  fileLog << "Console TopMatch Result" << endl;
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  fileLog << "Parameters:" << endl;
  fileLog << "S" << endl;
  fileLog << "  Number of residue pairs that are structurally equivalent" << endl;
  fileLog << "  (= length of the alignment, similarity)" << endl;
  fileLog << "s" << endl;
  fileLog << "  Relative similarity, expressed in percent" << endl;
  fileLog << "  (= 100 x (2 x S/( Lq+Lt)), where Lq and Lt are the lengths of the query and target sequences, respectively)" << endl;
  fileLog << "cq" << endl;
  fileLog << "  Relative cover of the query with respect to the target, expressed in percent" << endl;
  fileLog << "  (= 100 x S/Lq)" << endl;
  fileLog << "ct" << endl;
  fileLog << "  Relative cover of the target with respect to the query, expressed in percent" << endl;
  fileLog << "  (= 100 x S/Lt)" << endl;
  fileLog << "RMS" << endl;
  fileLog << "  Root-mean-square error of superposition in Ångströms, calculated using all structurally equivalent C-alpha atoms." << endl;
  fileLog << "Identity" << endl;
  fileLog << "  Sequence identity of query and target in the equivalent regions, expressed in percent." << endl;
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;

  fileLog << ShowTime(initialTime, buffer);
  fileLog.width(14); fileLog.right; fileLog << "        Target |";
  fileLog.width(14); fileLog.right; fileLog << "         Query |";
  fileLog.width(8);  fileLog.right; fileLog << "    Rank |";
  fileLog.width(8);  fileLog.right; fileLog << "       S |";
  fileLog.width(8);  fileLog.right; fileLog << "       s |";
  fileLog.width(8);  fileLog.right; fileLog << "      Cq |";
  fileLog.width(8);  fileLog.right; fileLog << "      Ct |";
  fileLog.width(8);  fileLog.right; fileLog << "     RMS |";
  fileLog.width(12); fileLog.right; fileLog << "   Similarity|";
  fileLog << endl;
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  
  int**    matrixS        = new int*[listFiles->Length()];
  int**    matrixs        = new int*[listFiles->Length()];
  int**    matrixCq       = new int*[listFiles->Length()];
  int**    matrixCt       = new int*[listFiles->Length()];
  double** matrixRMS      = new double*[listFiles->Length()];
  int**    matrixIdentity = new int*[listFiles->Length()];
  
  for (int i = 0; i < listFiles->Length(); i++) {
      
    matrixS[i]        = new int[listFiles->Length()];
    matrixs[i]        = new int[listFiles->Length()];
    matrixCq[i]       = new int[listFiles->Length()];
    matrixCt[i]       = new int[listFiles->Length()];
    matrixRMS[i]      = new double[listFiles->Length()];
    matrixIdentity[i] = new int[listFiles->Length()];
    
    fileOutS        << listFiles->Get(i)->Out() << " ";
    fileOuts        << listFiles->Get(i)->Out() << " ";
    fileOutCq       << listFiles->Get(i)->Out() << " ";
    fileOutCt       << listFiles->Get(i)->Out() << " ";
    fileOutRMS      << listFiles->Get(i)->Out() << " ";
    fileOutIdentity << listFiles->Get(i)->Out() << " ";
      
  }
  
  fileOutS        << endl;
  fileOuts        << endl;
  fileOutCq       << endl;
  fileOutCt       << endl;
  fileOutRMS      << endl;
  fileOutIdentity << endl;
    
  for (int i = 0, z = 0; i < listFiles->Length(); i++) {

    for (int j = i; j < listFiles->Length(); j++, z++) {
        
      if (z && !(z%DELAYPOSITION)) Wait(DELAYTIMESECOND);
      
      if (i == j) {
        
        matrixS[i][j]        = 100;  
        matrixs[i][j]        = 100;  
        matrixCq[i][j]       = 100;  
        matrixCt[i][j]       = 100;  
        matrixRMS[i][j]      = 0;  
        matrixIdentity[i][j] = 100;  
          
        fileOutS.width(4);        fileOutS.right;        fileOutS        << matrixS[i][j];
        fileOuts.width(4);        fileOuts.right;        fileOuts        << matrixs[i][j];
        fileOutCq.width(4);       fileOutCq.right;       fileOutCq       << matrixCq[i][j];
        fileOutCt.width(4);       fileOutCt.right;       fileOutCt       << matrixCt[i][j];
        fileOutRMS.width(4);      fileOutRMS.right;      fileOutRMS      << matrixRMS[i][j];
        fileOutIdentity.width(4); fileOutIdentity.right; fileOutIdentity << matrixIdentity[i][j];
        
        continue;
        
      }
        
      if (listFiles->Get(i)->Length() < 6 ) break;
      if (listFiles->Get(j)->Length() < 6 ) continue;
      
      char      url[1000];

      CURL*     curl;
      CURLcode  result;

      string    bufferURL;

      sprintf(url, URLTOPMATCH, listFiles->Get(i)->Out(), listFiles->Get(j)->Out());
      cout << "Retrieving " << url << endl;
      
      curl = curl_easy_init();

      if (curl) {

        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER,     errorBuffer);
        curl_easy_setopt(curl, CURLOPT_URL,             url);
        curl_easy_setopt(curl, CURLOPT_HEADER,          0);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION,  1);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,   writer);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA,       &bufferURL);

        result = curl_easy_perform(curl);
        curl_easy_cleanup(curl);

        if (result == CURLE_OK) {

          //cout << bufferURL << endl;
          Ming_init();


//          SWFShape *shape = new SWFShape();
//
//          shape->setLine(20, 0, 0, 0);
//          shape->movePenTo(100,100);
//          shape->drawLine(100,0);
//          shape->drawLine(0,100);
//          shape->drawLine(-100,0);
//          shape->drawLine(0,-100);
//
//          movie->add(shape);
//
//          movie->output();
//          
//          fstream fileZ;
//          fileZ.open("1.swf", fstream::out);
//          fileZ << bufferURL;
//          fileZ.close();
//
//          SWFMovie *movie = new SWFMovie();
//          movie->importCharacter("1.swf", "1");
//
//
//          //sswf_data_read(bufferURL, &buffer, &size);
//
//
//          gzFile testFile = gzopen("1.swf", "rb9");
//
//          char realBuffer[3001];
//
//          while (!gzeof(testFile)) {
//            gzread(testFile, realBuffer, 3000);
//            cout << realBuffer << endl;
//          }
//
//          gzclose(testFile);

          exit(0);

//          size_t  foundTV, foundRank, found, foundA;
//          string  str;
//
//          strcpy(buffer, listFiles->Get(i)->Out());
//          char* targetId = SplitColumn(buffer, 1, " ,");
//          strcpy(buffer, listFiles->Get(j)->Out());
//          char* queryId  = SplitColumn(buffer, 1, " ,");
//
//          str = "Rank";      foundRank = bufferURL.find(str);
//          str = targetId;    foundTV   = bufferURL.find(str, foundRank + 1);
//          str = ">";         found     = bufferURL.find(str, foundTV   + 1);
//          str = "</a></td>"; foundA    = bufferURL.find(str, found     + 1);
//
//          string rank = bufferURL.substr(found + 1, foundA - found - 1);
//
////        S
////            Number of residue pairs that are structurally equivalent
////            (= length of the alignment, similarity)
////        s̄
////            Relative similarity, expressed in percent
////            (= 100 x (2 x S/( Lq+Lt)), where Lq and Lt are the lengths of the query and target sequences, respectively)
////        cq
////            Relative cover of the query with respect to the target, expressed in percent
////            (= 100 x S/Lq)
////        ct
////            Relative cover of the target with respect to the query, expressed in percent
////            (= 100 x S/Lt)
////        RMS
////            Root-mean-square error of superposition in Ångströms, calculated using all structurally equivalent C-alpha atoms.
////        Identity
////            Sequence identity of query and target in the equivalent regions, expressed in percent.
////
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string S = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string s = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string cq = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string ct = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string RMS = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          str = "<td align=\"right\">"; found  = bufferURL.find(str, foundA + 1);
//          str = "</td>";                foundA = bufferURL.find(str, found  + 1);
//
//          string identity = bufferURL.substr(found + strlen("<td align=\"right\">"), foundA - found - strlen("<td align=\"right\">"));
//
//          matrixS[i][j]        = matrixS[j][i]        = atoi(S.data());
//          matrixs[i][j]        = matrixs[j][i]        = atoi(s.data());
//          matrixCq[i][j]       = matrixCq[j][i]       = atoi(cq.data());
//          matrixCt[i][j]       = matrixCt[j][i]       = atoi(ct.data());
//          matrixRMS[i][j]      = matrixRMS[j][i]      = atof(RMS.data());
//          matrixIdentity[i][j] = matrixIdentity[j][i] = atoi(identity.data());
//
//          ShowTime(initialTime);
//          cout.width(14); cout.right; cout << listFiles->Get(i)->Out() << " |";
//          cout.width(14); cout.right; cout << listFiles->Get(j)->Out() << " |";
//          cout.width(8);  cout.right; cout << rank                     << " |";
//          cout.width(8);  cout.right; cout << S                        << " |";
//          cout.width(8);  cout.right; cout << s                        << " |";
//          cout.width(8);  cout.right; cout << cq                       << " |";
//          cout.width(8);  cout.right; cout << ct                       << " |";
//          cout.width(8);  cout.right; cout << RMS                      << " |";
//          cout.width(12); cout.right; cout << identity                 << " |";
//          cout << endl;
//
//          fileLog << ShowTime(initialTime, buffer);
//          fileLog.width(14); fileLog.right; fileLog << listFiles->Get(i)->Out() << " |";
//          fileLog.width(14); fileLog.right; fileLog << listFiles->Get(j)->Out() << " |";
//          fileLog.width(8);  fileLog.right; fileLog << rank                     << " |";
//          fileLog.width(8);  fileLog.right; fileLog << S                        << " |";
//          fileLog.width(8);  fileLog.right; fileLog << s                        << " |";
//          fileLog.width(8);  fileLog.right; fileLog << cq                       << " |";
//          fileLog.width(8);  fileLog.right; fileLog << ct                       << " |";
//          fileLog.width(8);  fileLog.right; fileLog << RMS                      << " |";
//          fileLog.width(12); fileLog.right; fileLog << identity                 << " |";
//          fileLog << endl;
//
//          fileOutS.width(4);        fileOutS.right;        fileOutS        << matrixS[i][j];
//          fileOuts.width(4);        fileOuts.right;        fileOuts        << matrixs[i][j];
//          fileOutCq.width(4);       fileOutCq.right;       fileOutCq       << matrixCq[i][j];
//          fileOutCt.width(4);       fileOutCt.right;       fileOutCt       << matrixCt[i][j];
//          fileOutRMS.width(4);      fileOutRMS.right;      fileOutRMS      << matrixRMS[i][j];
//          fileOutIdentity.width(4); fileOutIdentity.right; fileOutIdentity << matrixIdentity[i][j];
          
        }

//        fileOutS        << endl;
//        fileOuts        << endl;
//        fileOutCq       << endl;
//        fileOutCt       << endl;
//        fileOutRMS      << endl;
//        fileOutIdentity << endl;

      }
      
    }
    
  }
  
  fileLog << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  cout    << "---------------------------------------------------------------------------------------------------------------------------------------------------" << endl;
  
  for (int i = 0; i < listFiles->Length(); i++) {
      
    if (matrixS[i])        delete[] matrixS[i];
    if (matrixs[i])        delete[] matrixs[i];
    if (matrixCq[i])       delete[] matrixCq[i];
    if (matrixCt[i])       delete[] matrixCt[i];
    if (matrixRMS[i])      delete[] matrixRMS[i];
    if (matrixIdentity[i]) delete[] matrixIdentity[i];
    
  }

  if (matrixS)        delete[] matrixS;
  if (matrixs)        delete[] matrixs;
  if (matrixCq)       delete[] matrixCq;
  if (matrixCt)       delete[] matrixCt;
  if (matrixRMS)      delete[] matrixRMS;
  if (matrixIdentity) delete[] matrixIdentity;

  fileOutS.close();
  fileOuts.close();
  fileOutCq.close();
  fileOutCt.close();
  fileOutRMS.close();
  fileOutIdentity.close();
  fileLog.close();

  if (namefileIn)  delete[] namefileIn;
  if (namefileOut) delete[] namefileOut;
  if (listFiles)   delete   listFiles;

  return 1;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//


